%{
open Core.Std
open Vlog
open Ast
%}

%token EOF
%token <int> Literal
%token <string> Ident
%token <int> Int

%token Amp
%token LParen
%token RParen
%token Plus
%token Comma
%token Minus
%token Dot
%token Colon
%token Semi
%token Eq
%token Quest
%token LBrack
%token RBrack
%token Caret
%token LBrace
%token Bar
%token RBrace
%token Tilde

%token K_assign
%token K_endmodule
%token K_input
%token K_module
%token K_output
%token K_reg
%token K_wire

%start <Ast.toplevel> top
%type <wire_reg> wire_reg
%type <(string * (wire_reg * (int * int))) list> wire_reg_decl
%type <Vlog.expr * Vlog.expr> assign
%type <string * (port_direction * (int * int))> port_spec
%type <Ast.decl> decl

%left Bar
%left Caret
%left Amp

%%

top:
  | K_module; name = Ident; LParen; ports = separated_list(Comma, port_spec); RParen; Semi; decls = list(decl); K_endmodule
    {{ mod_name = name; mod_ports = ports; mod_decls = decls }}

port_direction:
  | K_input  { Input  }
  | K_output { Output }

range_spec:
  | LBrack; hi_inc = Int; Colon; lo = Int; RBrack
    { lo, hi_inc-1 }

port_spec:
  | dir = port_direction; range_opt = range_spec?; name = Ident
    { name, (dir, Option.value range_opt ~default:(0,1)) }

assign_lhs:
  | sym = Ident
    { Vlog.Sym sym }
  | sym = Ident; range = range_spec
    { Vlog.Sym_part (sym, range) }

expr:
  | sym = Ident
    { Vlog.Sym sym }
  | sym = Ident; range = range_spec
    { Vlog.Sym_part (sym, range) }
  | e1 = expr; Amp; e2 = expr
    { Vlog.Binary (Vlog.And, e1, e2) }
  | e1 = expr; Bar; e2 = expr
    { Vlog.Binary (Vlog.Or, e1, e2) }
  | e1 = expr; Caret; e2 = expr
    { Vlog.Binary (Vlog.Xor, e1, e2) }
  (* | e_cond = expr; Quest; e_true = expr; Colon; e_false = expr
    { Vlog.Cond (e_cond, e_true, e_false) } *)

assign:
  | K_assign; lhs = assign_lhs; Eq; rhs = expr; Semi
    { lhs, rhs }

wire_reg:
  | K_wire { Wire }
  | K_reg  { Reg  }

wire_reg_decl:
  | kind = wire_reg; range = range_spec; names = separated_list(Comma, Ident); Semi
    { List.map names ~f:(fun name -> name, (kind, range)) }

decl:
  | decl = wire_reg_decl
    { Decl_wire_reg decl }
  | decl = assign
    { Decl_assign decl }
